新旧 S3 オブジェクトの差分を Lambda 関数で取得し SNS で通知してみた

新旧 S3 オブジェクトの差分を Lambda 関数で取得し SNS で通知してみた

Clock Icon2024.08.09

はじめに

テクニカルサポートの 片方 です。
バージョニング有効化済みの S3 バケットで新バージョンのオブジェクトがアップロードされた際に Lambda 関数を利用して差分を取得したいと思います。そして取得した差分を SNS で通知してみました。
構成としては以下のようになります。
スクリーンショット 2024-08-09 132221

やってみた

SNS トピックの作成、トピックのサブスクリプションの作成は省かせいただきます。
しかしながら、作成したトピックの arn は後ほど必要になるのでメモしてください。

https://docs.aws.amazon.com/ja_jp/sns/latest/dg/sns-getting-started.html

ロール

作成する Lamada 関数にアタッチする実行ロールを作成します。
Lambda 関数作成時に選択する、デフォルトの実行ロールの変更において、「基本的な Lambda アクセス権限で新しいロールを作成」を選択してください。

無題

その後、作成された当該ロールにカスタマー管理ポリシーとして以下をアタッチしてください。

ポリシー例
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket",
                "s3:GetObject",
                "s3:ListBucketVersions",
                "s3:GetObjectVersion"
            ],
            "Resource": [
                "arn:aws:s3:::対象バケット名",
                "arn:aws:s3:::対象バケット名/*"
            ]
        }
    ]
}

Lambda 関数

今回は Python 3.12 で作成しました。

https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sns.html

サンプルコード
import boto3
import difflib

s3 = boto3.client('s3')
sns = boto3.client('sns')

def lambda_handler(event, context):
    try:
        bucket = event['Records'][0]['s3']['bucket']['name']
        key = event['Records'][0]['s3']['object']['key']
    except KeyError:
        return {'message': 'Event structure is not as expected'}

    try:
        versions = s3.list_object_versions(Bucket=bucket, Prefix=key)
    except s3.exceptions.ClientError:
        return {'message': 'Failed to list object versions'}

    if 'Versions' not in versions or len(versions['Versions']) < 2:
        return {'message': 'No previous version available for comparison'}

    latest_versions = sorted(versions['Versions'], key=lambda v: v['LastModified'], reverse=True)[:2]

    latest_version_id = latest_versions[0]['VersionId']
    previous_version_id = latest_versions[1]['VersionId']

    latest_object = s3.get_object(Bucket=bucket, Key=key, VersionId=latest_version_id)['Body'].read().decode('utf-8')
    previous_object = s3.get_object(Bucket=bucket, Key=key, VersionId=previous_version_id)['Body'].read().decode('utf-8')

    diff = difflib.ndiff(previous_object.splitlines(), latest_object.splitlines())
    diff_result = '\n'.join(line for line in diff if line.startswith('+ ') or line.startswith('- '))

    message = f"""
S3 バケット名: {bucket}
オブジェクト名: {key}

変更箇所
{diff_result}

旧バージョン
{previous_object}
    """

    sns.publish(
        TopicArn='作成した SNS トピックの arn を記載',
        Message=message,
        Subject='S3 Object Version Difference Notification'
    )

    return {'message': 'Diff computed and notification sent'}

S3 イベント通知

バージョニング有効化済み対象 S3 バケットにおいてオブジェクトが更新された際に、イベント通知を利用して作成した Lambda 関数を呼び出します。

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/EventNotifications.html

対象 S3 バケットのプロパティを選択します。
無題

イベント通知を作成をクリックします。
無題

イベント名を記載して、イベントタイプは PUT のみ選択します。
無題

送信先セクションで Lambda 関数を選択し、作成した Lambda 関数を選択します。
無題

問題なければ、作成してください。
無題

これで、終了です。お疲れさまでした!

検証してみた

対象バケットに適当なオブジェクト(ファイル)をアップロードします。

無題

無題

ファイルの内容を変更します。
無題

変更後のオブジェクト(ファイル)を S3 バケットへアップロードします。その後、新旧バージョンが存在することを確認します。

無題

無題

SNS で設定した通知先へ届いていることを確認しました。成功です!

無題

通知例
※ 一部マスクします
S3 バケット名: test-20240523-1700
オブジェクト名: Sample-test.txt

変更箇所
+ text-ver2だよ。
+ 本日は晴天なり。
- textだよ。
- テキストだよ。
- テストだよ。

旧バージョン
textだよ。
テキストだよ。
テストだよ。

--
このトピックからの通知の受信を停止したい場合は、以下のリンクをクリックまたはアクセスして登録解除してください:
https://sns.ap-northeast-1.ama zonaws.com/unsubscribe.html? SubscriptionArn=arn:aws:sns: ap-northeast-1:123456789012: Test-Event:7edxxxxx-1114-4444- 98b3absde123456c4&Endpoint=aaaaaa.bbbbb@xxxxxxx.jp

このメールに直接返信しないでください。このメールに関してご質問やご意見がございましたら、https://aws.amazon.com/supportまでお問い合わせください。

補足

例えば、以下のような記述を追加することで、現在のオブジェクト内容も通知に含むことが可能です。

message = f"""
S3 バケット名: {bucket}
オブジェクト名: {key}

変更箇所
{diff_result}

旧バージョン
{previous_object}

新バージョン
{latest_object}

まとめ

環境変数を利用して、SNS で通知する方法などもご検討ください。また、ご自身の環境に合わせて通知文などを適宜修正の上ご利用ください。
本ブログが誰かのご参考になれば幸いです。

参考資料

アノテーション株式会社について

アノテーション株式会社は、クラスメソッド社のグループ企業として「オペレーション・エクセレンス」を担える企業を目指してチャレンジを続けています。「らしく働く、らしく生きる」のスローガンを掲げ、様々な背景をもつ多様なメンバーが自由度の高い働き方を通してお客様へサービスを提供し続けてきました。現在当社では一緒に会社を盛り上げていただけるメンバーを募集中です。少しでもご興味あれば、アノテーション株式会社WEBサイトをご覧ください。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.